//
//  Network.m
//  netWorkDefine
//
//  Created by VierGhost on 15/9/1.
//  Copyright (c) 2015年 dingli. All rights reserved.
//

#import "Network.h"
#import <AFNetworkActivityIndicatorManager.h>

static dispatch_semaphore_t IdentifyLock;

@interface Network ()

@property (strong, nonatomic) NSMutableDictionary<NSNumber *, NSURLSessionDataTask *> *dispathCache;
@property (strong, nonatomic) NSMutableDictionary *urlCache;

@end

@implementation Network {
    Network *response;
    NSHTTPURLResponse *myTask;
}

+ (NSString *)network_ObtionURL:(NSString *)url {
    
    if ([url containsString:@"http"]) {
        return url;
    }
    else if ([FBTweak bn_clientOnline]) {
        url = [NSString stringWithFormat:@"%@%@",ALHostAddress,url];
    }
    else if ([FBTweak bn_clientPersonalTestService]) {
        url = [NSString stringWithFormat:@"%@%@",ALPersonalTestHostAddress,url];
    }
    else if (![NSString mdf_isEmptyString:[FBTweak bn_clientConfigBaseUrl]]) {
        url = [NSString stringWithFormat:@"%@%@",[FBTweak bn_clientConfigBaseUrl],url];
    }
    else {
        url = [NSString stringWithFormat:@"%@%@",ALReleaseHostAddress,url];
    }
    
    return url;
}

+ (void)link_body:(NSString *)url parameters:(NSDictionary *)params outOfTime:(float)outTime callback:(void (^)(id responseObject))callback {
    
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
 
    configuration.timeoutIntervalForResource = (outTime<=0.f || !outTime) ? timeoutTime : outTime;
    configuration.HTTPShouldUsePipelining = true;
    
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
    
    NSString *jsonStr = [NSString mdf_jsonValueToDic:params];
    NSData *postData = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
    
    url = [self network_ObtionURL:url];
    
    NSURL *URL = [NSURL URLWithString:url];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
    
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:postData];
    
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
    NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
        
        [self log:url Params:@{@"json":jsonStr} Response:responseObject];
        
        if(callback != nil) {
            callback(responseObject);
        }
    }];
    [dataTask resume];
}

#pragma mark - 实例化自身管理内容
+ (Network *)sharedJsonRequest {
    static Network *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        IdentifyLock = dispatch_semaphore_create(1);
        sharedInstance = [[self alloc] init];
        sharedInstance.dispathCache = [NSMutableDictionary dictionary];
        sharedInstance.urlCache = [NSMutableDictionary dictionary];
    });
    return sharedInstance;
}

#pragma mark - 附着动画处理内容
+ (BOOL)link:(NSString *)url parameters:(NSDictionary *)params outOfTime:(float)outTime callback:(void (^)(Network *))callback {
    
    Network *request = [self sharedJsonRequest];
    return [request _link:url parameters:params outOfTime:outTime callback:callback showAlert:YES];
}

#pragma mark - 无附着动画处理内容
+ (BOOL)linkWithoutLoadingAnimation:(NSString *)url parameters:(NSDictionary *)params outOfTime:(float)outTime callback:(void (^)(Network *))callback {
    
    Network *request = [self sharedJsonRequest];
    return [request _link:url parameters:params outOfTime:outTime callback:callback showAlert:NO];
}

#pragma mark - 图片处理请求
+ (BOOL)link:(NSString*)url parameters:(NSDictionary *)params imgParameters:(NSArray *)imgParameters fileNames:(NSArray *)fileNames materialtype:(NSInteger)materialtype outOfTime:(float)outTime callback:(REQUEST_CALLBACK)callback
{
    Network *request = [self sharedJsonRequest];
    return [request _link:url parameters:params imgParameters:imgParameters fileNames:fileNames materialtype:materialtype outOfTime:outTime callback:callback showAlert:YES];
}

#pragma mark - GET、POST 请求
- (BOOL)_link:(NSString*)url parameters:(NSDictionary *)params outOfTime:(float)outTime callback:(REQUEST_CALLBACK)callback showAlert:(BOOL)showAl {
    
    url = [Network network_ObtionURL:url];
    
    //打印请求地址
    AFHTTPSessionManager *manager = [self createManagerWithURL:url overTime:outTime];
    
    [manager setSecurityPolicy:[self customSecurityPolicy]];
    
    [self beforeLink:manager ani:showAl];
    
    NSURLSessionDataTask *dataTask = nil;
    
    if (params) {
        dataTask = [manager POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
                        } success:^(NSURLSessionDataTask *task, id responseObject) {
                            
                            [self collectionResponseWithDataTask:task params:params responseObject:responseObject];
                            
                            [self log:url Params:params Response:responseObject];
                            
                            myTask = (NSHTTPURLResponse *)task.response;
                            [self configResponseWithTask:myTask originTask:task responseObject:responseObject error:nil ani:showAl];
                            
                            if(callback != nil) {
                                callback(response);
                            }
                        } failure:^(NSURLSessionDataTask *task, NSError *error) {

                            [self collectionResponseWithDataTask:task params:params responseObject:nil];

                            [self log:url Params:params Response:error];
                            
                            myTask = (NSHTTPURLResponse *)task.response;
                            [self configResponseWithTask:myTask originTask:task responseObject:nil error:error ani:showAl];
                            if (showAl) [self afterLink:response];
                            
                            if(callback != nil) {
                                callback(response);
                            }
                        }];
    } else {
        dataTask = [manager GET:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
                        } success:^(NSURLSessionDataTask *task, id responseObject) {

                            [self collectionResponseWithDataTask:task params:params responseObject:responseObject];

                            [self log:url Params:params Response:responseObject];
                            
                             myTask = (NSHTTPURLResponse *)task.response;
                             [self configResponseWithTask:myTask originTask:task responseObject:responseObject error:nil ani:showAl];
                            
                             if(callback != nil) {
                                 callback(response);
                             }
                        } failure:^(NSURLSessionDataTask *task, NSError *error) {

                            [self collectionResponseWithDataTask:task params:params responseObject:nil];

                            [self log:url Params:params Response:error];
                            
                             myTask = (NSHTTPURLResponse *)task.response;
                             [self configResponseWithTask:myTask originTask:task responseObject:nil error:error ani:showAl];
                             if (showAl) [self afterLink:response];
                            
                             if(callback != nil) {
                                 callback(response);
                             }
                        }];
    }

    //构建缓存
    [self dispatchTask:dataTask];
    //请求动画处理
    if (showAl) {
        //这里时间设置上很讨巧   因为不是完美版本    所以难免会出现不同接口的响应被扰乱    推荐设置0.5~1s内   应该把动画部分摘出去的
        NSTimer *timer = [NSTimer timerWithTimeInterval:aniDelayTime target:self selector:@selector(handleLoadingTimer:) userInfo:@{@"currentTask":dataTask} repeats:NO];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        
        [[UIApplication sharedApplication].keyWindow.rootViewController.view endEditing:YES];
    }
    
    return YES;
}
- (void)handleLoadingTimer:(NSTimer *)theTimer {
    NSURLSessionDataTask *task = [theTimer.userInfo objectForKey:@"currentTask"];
    
    if ([self.dispathCache objectForKey:@(task.taskIdentifier)] && task.state==NSURLSessionTaskStateRunning) {
        dispatch_async(dispatch_get_main_queue(), ^{
            
            [SVProgressHUD showWithStatus:@"加载中..."];
        });
    }
    
    [theTimer invalidate];
    theTimer = nil;
}

#pragma mark - 上传图片
- (BOOL)_link:(NSString*)url parameters:(NSDictionary *)params imgParameters:(NSArray *)imgParameters fileNames:(NSArray *)fileNames materialtype:(NSInteger)materialtype outOfTime:(float)outTime callback:(REQUEST_CALLBACK)callback showAlert:(BOOL)showAl {

    url = [Network network_ObtionURL:url];
    
    AFHTTPSessionManager *manager = [self createManagerWithURL:url overTime:outTime];
    
    if (materialtype != 100) {  // 不是人脸识别
#warning https使用
        [manager setSecurityPolicy:[self customSecurityPolicy]];
    }
    NSURLSessionDataTask *dataTask = nil;

    dataTask = [manager POST:url parameters:params constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
        
        for (int i = 1; i <= imgParameters.count; i++) {
            NSData *data1 = imgParameters[i - 1];
            
            NSString *s = fileNames[i - 1];
            
            [formData appendPartWithFileData:data1 name:[NSString stringWithFormat:@"%@",s] fileName:[NSString stringWithFormat:@"%@.png",s] mimeType:@"image/png"];
            
        }
        
    } progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
    
        [self collectionResponseWithDataTask:task params:params responseObject:responseObject];

        [self log:url Params:params Response:responseObject];
        
        myTask = (NSHTTPURLResponse *)task.response;
        [self configResponseWithTask:myTask originTask:task responseObject:responseObject error:nil ani:YES];
        
        if(callback != nil) {
            callback(response);
        }
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        
        [self collectionResponseWithDataTask:task params:params responseObject:nil];

        [self log:url Params:params Response:error];
        
        myTask = (NSHTTPURLResponse *)task.response;
        [self configResponseWithTask:myTask originTask:task responseObject:nil error:error ani:YES];
        if (showAl) [self afterLink:response];
        
        if(callback != nil) {
            callback(response);
        }
    }];
    //构建缓存
    [self dispatchTask:dataTask];
    //请求动画处理
    if (showAl) {
        //这里时间设置上很讨巧   因为不是完美版本    所以难免会出现不同接口的响应被扰乱    推荐设置0.5~1s内   应该把动画部分摘出去的
        NSTimer *timer = [NSTimer timerWithTimeInterval:aniDelayTime target:self selector:@selector(handleLoadingTimer:) userInfo:@{@"currentTask":dataTask} repeats:NO];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        
        [[UIApplication sharedApplication].keyWindow.rootViewController.view endEditing:YES];
    }
    
    return YES;
}

- (NSString *)configTestServerURL:(NSString *)url {
    
    return url;
}

#pragma mark - 
#pragma mark - NetworkInterface 网络请求入口
#pragma mark - 创建manager类
- (AFHTTPSessionManager *)createManagerWithURL:(NSString *)URLStr overTime:(NSTimeInterval)outTime {

    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
    
    config.timeoutIntervalForResource = (outTime<=0.f || !outTime) ? timeoutTime : outTime;
    config.HTTPShouldUsePipelining = true;
    
    // token
    if ([BNAccount user_Token]) {
        config.HTTPAdditionalHeaders = @{@"token":[BNAccount user_Token]};
    }

    AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:URLStr] sessionConfiguration:config];
    //manager Setting
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    manager.securityPolicy = [AFSecurityPolicy defaultPolicy];
    //contentType
    NSSet *contenType = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
    manager.responseSerializer.acceptableContentTypes = contenType;
    //JsonCleaner
    ((AFJSONResponseSerializer *)manager.responseSerializer).removesKeysWithNullValues = YES;
    
    return manager;
}

#pragma mark - url、task 缓存管理 GET、POST
- (void)dispatchTask:(NSURLSessionDataTask *)task {
    
    if (task == nil) { return; }
    dispatch_semaphore_wait(IdentifyLock, DISPATCH_TIME_FOREVER);
    [self.dispathCache setObject:task forKey:@(task.taskIdentifier)];
    dispatch_semaphore_signal(IdentifyLock);
    [task resume];
    
    return;
}

#pragma mark - 终止当前全部请求任务
- (void)cancelAllTask {
    
    for (NSURLSessionTask *task in self.dispathCache.allValues) {
        [task cancel];
    }
    dispatch_semaphore_wait(IdentifyLock, DISPATCH_TIME_FOREVER);
    [self.dispathCache removeAllObjects];
    dispatch_semaphore_signal(IdentifyLock);
}

- (void)cancelTaskWithTaskIdentifier:(NSNumber *)taskIdentifier {
    
    [self.dispathCache[taskIdentifier] cancel];
    
    dispatch_semaphore_wait(IdentifyLock, DISPATCH_TIME_FOREVER);
    [self.dispathCache removeObjectForKey:taskIdentifier];
    dispatch_semaphore_signal(IdentifyLock);
}

#pragma mark - 请求结果处理
- (void)configResponseWithTask:(NSHTTPURLResponse *)task
                    originTask:(NSURLSessionDataTask *)oriTask
                responseObject:(id)responseObject
                         error:(NSError *)error
                           ani:(BOOL)showAl {
    
    response = [[Network alloc] init];
    response.status = task.statusCode;
    response.dataDic = responseObject;
    
    //删除缓存
    dispatch_semaphore_wait(IdentifyLock, DISPATCH_TIME_FOREVER);
    [self.dispathCache removeObjectForKey:@(oriTask.taskIdentifier)];
    dispatch_semaphore_signal(IdentifyLock);
    
    NSInteger code = responseObject[@"code"]?[responseObject[@"code"] integerValue]:response.error.code;
    response.responCode = code;
    response.message = [responseObject objectForKey:@"message"] ?: @"未知错误，请稍后重试";

    if (error) {
        response.header = nil;
    } else {
        response.header = [task allHeaderFields];
    }
    response.error = error;
    
    if (showAl) {
        [SVProgressHUD dismiss];
    }
}

#pragma mark - 请求派发前处理
- (void)beforeLink:(AFHTTPSessionManager *)manager ani:(BOOL)showAl {
    NSOperationQueue *operationQueue = manager.operationQueue;
    
    [manager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusReachableViaWWAN:
            case AFNetworkReachabilityStatusReachableViaWiFi:
                [operationQueue setSuspended:NO];
                break;
            case AFNetworkReachabilityStatusNotReachable:
            default:
                break;
        }
    }];
    
    [manager.reachabilityManager startMonitoring];
}

#pragma mark - 请求派发后处理
- (BOOL)afterLink:(Network *)myResponse {
    
    if (myResponse.status == 500) {
        [SVProgressHUD showErrorWithStatus:@"请求失败，请稍后重试"];
        return TRUE;
    }
    
    if (myResponse.error == nil) {
        return TRUE;
    }
    
    if ([myResponse.error code] == NSURLErrorNotConnectedToInternet) {
        [SVProgressHUD showErrorWithStatus:@"网络不佳，请稍后重试"];

        return FALSE;
    }
    
    [SVProgressHUD showErrorWithStatus:@"网络不佳，请稍后重试"];
    return true;
}

#pragma mark - 分析工具
/** 拼接请求地址，便于追寻请求错误 */
- (NSString *)configHttpURLStringWithURLStr:(NSString *)urlStr parameters:(NSDictionary *)parameters {
    __block NSString *newURLStr = urlStr;
    
    newURLStr = [newURLStr hasSuffix:@"?"] ? newURLStr : [newURLStr stringByAppendingString:@"?"];
    [parameters enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        newURLStr = [newURLStr stringByAppendingFormat:@"&%@=%@", key, obj];
    }];
    if (newURLStr.length > 0) {
        newURLStr = [newURLStr stringByReplacingOccurrencesOfString:@"?&" withString:@"?"];
    }
    if ([newURLStr hasSuffix:@"?"] || [newURLStr hasSuffix:@"&"]) {
        newURLStr = [newURLStr substringToIndex:newURLStr.length - 1];
    }
    return newURLStr;
}

#pragma mark - 验证证书
- (AFSecurityPolicy*)customSecurityPolicy
{
    // /先导入证书
    if (!self.certData) {
        NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"dkcs" ofType:@"cer"];//证书的路径
        self.certData = [NSData dataWithContentsOfFile:cerPath];
    }
    
    // AFSSLPinningModeCertificate 使用证书验证模式
    AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeCertificate];
    
    // allowInvalidCertificates 是否允许无效证书（也就是自建的证书），默认为NO
    // 如果是需要验证自建证书，需要设置为YES
    securityPolicy.allowInvalidCertificates = YES;
    
    //validatesDomainName 是否需要验证域名，默认为YES；
    //假如证书的域名与你请求的域名不一致，需把该项设置为NO；如设成NO的话，即服务器使用其他可信任机构颁发的证书，也可以建立连接，这个非常危险，建议打开。
    //置为NO，主要用于这种情况：客户端请求的是子域名，而证书上的是另外一个域名。因为SSL证书上的域名是独立的，假如证书上注册的域名是www.google.com，那么mail.google.com是无法验证通过的；当然，有钱可以注册通配符的域名*.google.com，但这个还是比较贵的。
    //如置为NO，建议自己添加对应域名的校验逻辑。
    securityPolicy.validatesDomainName = NO;
    
    NSSet *set = [[NSSet alloc] initWithObjects:self.certData, nil];
    
    //    securityPolicy.pinnedCertificates = @[certData];
    securityPolicy.pinnedCertificates = set;
    
    return securityPolicy;
}

- (void)collectionResponseWithDataTask:(NSURLSessionDataTask *)task
                                params:(NSDictionary *)params
                        responseObject:(id)responseObject {
    [Network collectionResponseWithDataTask:task params:params responseObject:responseObject];
}

+ (void)collectionResponseWithDataTask:(NSURLSessionDataTask *)task
                                params:(NSDictionary *)params
                        responseObject:(id)responseObject {
    
    if (FB_TWEAK_ENABLED) {
        NSDictionary *urlDict = task.originalRequest.URL.absoluteString.paramsFromURL;
        if (urlDict.count > 0) {
            NSMutableDictionary *dict = [NSMutableDictionary dictionary];
            
            dict[@"baseUrl"] = [NSString stringWithFormat:@"%@://%@", urlDict[@"PROTOCOL"], urlDict[@"HOST"]];
            dict[@"address"] = urlDict[@"URI"];
            dict[@"params"] = params;
            dict[@"parseCls"] = nil;
            dict[@"error"] = task.error;
            dict[@"responseObject"] = responseObject;
            
            if (task.error) {
                dict[@"hasError"] = @(YES);
            } else {
                dict[@"hasError"] = @(NO);
            }
            
            [[MDFRecentRequestHelper sharedInstance] addRequestInfoWithDictionary:dict];
        }
    }
}

+ (void)log:(NSString *)url Params:(NSDictionary *)params Response:(id)response {
    if ([FBTweak bn_clientAnalyticsUtil]) {
        NSLog(@"URL:%@ params:%@\n%@",url,params,response);
    }
}

- (void)log:(NSString *)url Params:(NSDictionary *)params Response:(id)response {
    [Network log:url Params:params Response:response];
}

@end
